from rest_framework import serializers
from datetime import datetime
import random
from .models import Order, OrderDetail
from django.db import transaction
from django_redis import get_redis_connection
from courses.models import Course, CourseExpire
from coupon.models import Coupon, UserCoupon
from luffyapi.settings import constants


class OrderModelSerializer(serializers.ModelSerializer):
    """订单信息的序列化器"""

    class Meta:
        model = Order
        fields = ["order_number", "pay_type", "credit", "coupon"]
        read_only_fields = ["order_number"]
        extra_kwargs = {
            "pay_type": {"write_only": True, "required": True},
            "credit": {"write_only": True},
            "coupon": {"write_only": True},
        }

    def validate(self, attrs):
        # 校验是否使用了积分
        credit = attrs.get("credit")
        if credit > self.context["request"].user.credit:
            raise serializers.ValidationError("积分使用超过拥有的数量！")

        # 校验是否使用了优惠券，优惠券在有效使用期间
        user_coupon_id = attrs.get("coupon")
        if user_coupon_id > 0:
            now = datetime.now()
            try:
                usercoupon = UserCoupon.objects.get(pk=user_coupon_id, start_time__lte=now)
                if now.timestamp() > usercoupon.end_time.timestamp():
                    raise serializers.ValidationError("优惠券已经过期或者没有达到使用时间！")
            except:
                raise serializers.ValidationError("优惠券找不到！")

        return attrs

    def create(self, validated_data):
        """保存订单"""

        # 在序列化器中，如果要获取视图中的数据可以通过 self.context["request"] self.context["view"]
        # 获取用户
        user = self.context["request"].user
        # 生成订单号 = 时间+用户ID+随机数
        order_number = datetime.now().strftime("%Y%m%d%H%M%S") + ("%06d" % user.id) + ("%04d" % random.randint(0, 9999))

        with transaction.atomic():  # 开启事务的自动提交
            # 设置回滚点
            save_id = transaction.savepoint()
            # 1. 生成订单
            try:
                coupon = validated_data.get("coupon")
                credit = validated_data.get("credit")
                order = Order.objects.create(
                    order_title="路飞学城课程购买",
                    total_price=0,
                    real_price=0,
                    order_number=order_number,
                    pay_type=validated_data.get("pay_type"),
                    credit=credit,
                    coupon=coupon,
                    user_id=user.id,
                )

                # 2. 连接redis
                redis_conn = get_redis_connection("cart")

                # 3. 到redis中提取对应用户的购物车
                cart_hash = redis_conn.hgetall("cart_%s" % user.id)
                selected_set = redis_conn.smembers("selected_%s" % user.id)

                if selected_set is None:
                    transaction.savepoint_rollback(save_id)
                    return serializers.ValidationError("对不起，购物车中没有勾选的商品，赶紧去买买买！")

                # 4. 提取购物车中勾选的商品信息
                total_price = 0
                pipe = redis_conn.pipeline()
                pipe.multi()
                for course_id_bytes in selected_set:
                    course_id = course_id_bytes.decode()
                    expire = int(cart_hash[course_id_bytes].decode())

                    try:
                        course = Course.objects.get(pk=course_id, is_show=True, is_delete=False)
                    except Course.DoesNotExist:
                        # 如果当前商品被下架或者被逻辑删除了则不显示到购物车商品列表中
                        continue

                    try:
                        ret = CourseExpire.objects.get(course_id=course_id, expire=expire)
                        expire = ret.expire
                    except:
                        expire = 0

                    # 5. 把循环中每一个商品添加到订单详情里面
                    real_price = course.get_discount_price_by_expire(expire)
                    OrderDetail.objects.create(
                        order=order,
                        course=course,
                        expire=expire,
                        price=course.get_price_by_expire(expire),
                        real_price=real_price,
                        discount_name=course.discount_name
                    )

                    # 6. 从redis中删除对应已经添加到订单里面的商品
                    pipe.hdel("cart_%s" % user.id, course_id)
                    pipe.srem("selected_%s" % user.id, course_id)

                    # 计算商品总价
                    total_price += float(real_price)

                # 执行redis的事务管道
                pipe.execute()

                # 判断客户端如果使用了优惠券，则计算优惠券的折扣价格
                coupon_price = 0
                if coupon > 0:
                    # 客户端使用了优惠券
                    usercoupon = UserCoupon.objects.get(pk=coupon)
                    if total_price < usercoupon.coupon.condition:
                        transaction.savepoint_rollback(save_id)
                        serializers.ValidationError("对不起，您本次订单使用的优惠券不满足使用条件，赶紧去买买买！")

                    # 计算优惠券的抵扣价格
                    sale = float(usercoupon.coupon.sale[1:])
                    if usercoupon.coupon.coupon_type == 1:
                        """减免优惠券"""
                        coupon_price = sale
                    else:
                        """折扣优惠券"""
                        coupon_price = total_price * (1 - sale)

                    # 下单以后，如果使用了优惠券，则改变当前优惠券的状态
                    # 将来如果用户对于当前订单进行了取消，或者系统超时取消，则还原当前优惠券的状态即可
                    usercoupon.is_use = True
                    usercoupon.save()

                credit_price = 0
                if credit > 0:
                    """使用了积分"""
                    if (total_price - coupon_price) * constants.RMB_CREDIT < credit:
                        """如果积分使用数额超过订单能兑换的最大数额，也有问题"""
                        transaction.savepoint_rollback(save_id)
                        raise serializers.ValidationError("生成订单失败！")

                    # 扣除积分
                    credit_price = credit / constants.RMB_CREDIT
                    user.credit -= credit
                    user.save()

                # 更新订单的总价
                order.total_price = total_price  # 订单总价，不涉及积分和优惠券的抵扣
                order.real_price = float(
                    "%.2f" % (total_price - coupon_price - credit_price))  # 订单实付价，在订单总价的基础上，扣除了积分和优惠券
                order.save()

            except:
                transaction.savepoint_rollback(save_id)
                raise serializers.ValidationError("生成订单失败！")

        # 6. 返回订单的生成结果
        return order
